package com.yuba.front.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yuba.common.core.constant.ActionTypes;
import com.yuba.common.core.constant.Constants;
import com.yuba.common.core.constant.RecommendScore;
import com.yuba.common.core.constant.UserConstants;
import com.yuba.common.core.exception.front.FrontException;
import com.yuba.common.core.utils.MapstructUtils;
import com.yuba.common.core.utils.StringUtils;
import com.yuba.common.core.utils.ValidatorUtils;
import com.yuba.common.core.validate.AddGroup;
import com.yuba.common.mybatis.core.page.PageQuery;
import com.yuba.common.mybatis.core.page.TableDataInfo;
import com.yuba.common.satoken.utils.LoginHelper;
import com.yuba.front.domain.*;
import com.yuba.front.domain.bo.PostsBo;
import com.yuba.front.domain.bo.PostsPushBo;
import com.yuba.front.domain.param.DelParam;
import com.yuba.front.domain.param.QueryParam;
import com.yuba.front.domain.vo.CollectPostsVo;
import com.yuba.front.domain.vo.PostsHotVo;
import com.yuba.front.domain.vo.PostsListVo;
import com.yuba.front.domain.vo.PostsVo;
import com.yuba.front.event.UpdatePostsEvent;
import com.yuba.front.mapper.*;
import com.yuba.front.service.IPostsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 帖子Service业务层处理
 *
 * @author modakai
 * @date 2024-12-02
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class PostsServiceImpl implements IPostsService {

    private final PostsMapper baseMapper;
    private final CollectPostsMapper collectPostsMapper;
    private final CommentPostsMapper commentPostsMapper;
    private final LikePostsMapper likePostsMapper;
    private final TagsMapper tagsMapper;
    private final FrontUserMapper userMapper;

    /**
     * 查询帖子
     *
     * @param postId 主键
     * @return 帖子
     */
    @Override
    public PostsVo queryById(Long postId) {
        return baseMapper.selectVoById(postId);
    }

    /**
     * 分页查询帖子列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 帖子分页列表
     */
    @Override
    public TableDataInfo<PostsVo> queryPageList(PostsBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<Posts> lqw = buildQueryWrapper(bo);
        Page<PostsVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        result.getRecords().forEach(postsVo -> {
            postsVo.setTags(tagsMapper.selectPostTagList(postsVo.getPostId()));
        });
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的帖子列表
     *
     * @param bo 查询条件
     * @return 帖子列表
     */
    @Override
    public List<PostsVo> queryList(PostsBo bo) {
        LambdaQueryWrapper<Posts> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<Posts> buildQueryWrapper(PostsBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<Posts> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserId() != null, Posts::getUserId, bo.getUserId());
        lqw.eq(bo.getCategoryId() != null, Posts::getCategoryId, bo.getCategoryId());
        lqw.eq(StringUtils.isNotBlank(bo.getTitle()), Posts::getTitle, bo.getTitle());
        lqw.eq(StringUtils.isNotBlank(bo.getContent()), Posts::getContent, bo.getContent());
        lqw.eq(bo.getLikeCount() != null, Posts::getLikeCount, bo.getLikeCount());
        lqw.eq(bo.getViewCount() != null, Posts::getViewCount, bo.getViewCount());
        lqw.eq(bo.getCollectCount() != null, Posts::getCollectCount, bo.getCollectCount());
        lqw.eq(bo.getCommentCount() != null, Posts::getCommentCount, bo.getCommentCount());
        lqw.eq(bo.getCover() != null, Posts::getCover, bo.getCover());
        lqw.eq(StringUtils.isNotBlank(bo.getChoiceFlag()), Posts::getChoiceFlag, bo.getChoiceFlag());
        lqw.eq(StringUtils.isNotBlank(bo.getPinnedFlag()), Posts::getPinnedFlag, bo.getPinnedFlag());
        lqw.eq(StringUtils.isNotBlank(bo.getReleaseFlag()), Posts::getReleaseFlag, bo.getReleaseFlag());
        lqw.eq(StringUtils.isNotBlank(bo.getShowFlag()), Posts::getShowFlag, bo.getShowFlag());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), Posts::getStatus, bo.getStatus());
        lqw.eq(bo.getEditTime() != null, Posts::getEditTime, bo.getEditTime());
        return lqw;
    }

    /**
     * 新增帖子
     *
     * @param bo 帖子
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(PostsBo bo) {
        Posts add = MapstructUtils.convert(bo, Posts.class);
        validEntityBeforeSave(add);
        bo.setRecommendScore(1);
        bo.setRecommendTime(Instant.now().getEpochSecond());
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setPostId(add.getPostId());
        }
        return flag;
    }

    /**
     * 修改帖子
     *
     * @param bo 帖子
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(PostsBo bo) {
        Posts update = MapstructUtils.convert(bo, Posts.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(Posts entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除帖子信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    public Map<String, Object> apiQueryPageList(QueryParam queryParam) {
        Map<String, Object> result = new HashMap<>();

        List<PostsListVo> postsList = baseMapper.selectPageList(queryParam);
        Set<Long> like = new HashSet<>();
        Set<Long> collect = new HashSet<>();
        if (LoginHelper.isLogin()) {
            // 登录用户，判断是否收藏
            Long userId = LoginHelper.getUserId();
            // 获取点赞和收藏列表
            like = likePostsMapper
                .selectList(new LambdaQueryWrapper<LikePosts>().eq(LikePosts::getUserId, userId))
                .stream().map(LikePosts::getPostId).collect(Collectors.toSet());

            collect = collectPostsMapper
                .selectVoList(new LambdaQueryWrapper<CollectPosts>().eq(CollectPosts::getUserId, userId))
                .stream().map(CollectPostsVo::getPostId).collect(Collectors.toSet());
        }

        // 处理结果，如果需要，可以在这里添加更多的逻辑
        result.put("postsList", postsList);
        // 判断是否还有更多数据
        boolean hasMore = false;
        if (!postsList.isEmpty()) {
            hasMore = true;
            // 添加标签列表
            for (PostsListVo postsListVo : postsList) {
                List<String> tags = tagsMapper.selectPostTagList(postsListVo.getPostId());
                postsListVo.setTagList(tags);

                // 判断是否点赞
                postsListVo.setLikeFlag(like.contains(postsListVo.getPostId()));
                // 判断是否收藏
                postsListVo.setCollectFlag(collect.contains(postsListVo.getPostId()));
            }
        }
        result.put("hasMore", hasMore);

        return result;
    }

    @Override
    public List<PostsHotVo> apiQueryHotList(Long categoryId) {
        List<PostsHotVo> result = baseMapper.selectVoHotList(categoryId);
        // 搜索收藏标注
        Set<Long> postsIdSet = collectPostsMapper.selectVoList(new LambdaQueryWrapper<CollectPosts>()
            .eq(CollectPosts::getUserId, LoginHelper.getUserId())).stream().map(CollectPostsVo::getPostId).collect(Collectors.toSet());

        for (PostsHotVo postsHotVo : result) {
            postsHotVo.setCollectFlag(postsIdSet.contains(postsHotVo.getPostId()));
        }
        return result;
    }

    @Override
    public PostsVo apiDetail(Long postsId) {
        PostsVo postsVo = queryById(postsId);
        if (ObjectUtils.isEmpty(postsVo)) {
            return null;
        }
        // 用户信息
        FrontUser frontUser = userMapper.selectById(postsVo.getUserId());
        postsVo.setUsername(frontUser.getUsername());
        postsVo.setAvatar(frontUser.getAvatar());
        // 标签列表
        List<String> tags = tagsMapper.selectPostTagList(postsId);
        postsVo.setTags(tags);
        return postsVo;
    }


    @Override
    public void postsView(Long postsId) {
        // 获取对应的帖子详情
        PostsVo postsVo = queryById(postsId);
        if (ObjectUtils.isEmpty(postsVo)) {
            return;
        }

        // TODO 如何后期需要通过用户阅览的标签进行个性化推荐的话，就需求查询用户信息

        PostsBo postsBo = new PostsBo();
        postsBo.setPostId(postsId);
        // 更新浏览量
        postsBo.setViewCount(postsVo.getViewCount() + 1);
        // 更新推荐分 浏览量算一分
        postsBo.setRecommendScore(postsVo.getRecommendScore() + 1);

        // 无需事物，丢失了也没关系
        Boolean update = updateByBo(postsBo);
        if (!update) {
            log.error("更新浏览量失败，postsId:{}", postsId);
        }
    }

    @Override
    public PostsVo getDraft() {
        LambdaQueryWrapper<Posts> lqw = new LambdaQueryWrapper<>();
        lqw
            .eq(Posts::getUserId, LoginHelper.getUserId())
            .eq(Posts::getCategoryId, 0)
            .eq(Posts::getReleaseFlag, Constants.NO);
        return baseMapper.selectVoOne(lqw);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void push(PostsPushBo postsPush) {
        Posts posts = new Posts();
        // 1. 检测是否存在对应的帖子
        // 如果前端传递的postsId 就说明以及存在有对应的帖子
        Long postsId = postsPush.getPostsId();
        if (ObjectUtils.isEmpty(postsId) || (!ObjectUtils.isEmpty(postsId) && UserConstants.YES.equals(postsPush.getReleaseFlag()))) {
            // 设置推荐时间
            posts.setRecommendScore(1);
            Instant timestamp = Instant.now();
            posts.setRecommendTime(timestamp.getEpochSecond());
        }
        // 如果是发布的话，就校验数据是否完整
        if (UserConstants.YES.equals(postsPush.getReleaseFlag()) && postsPush.getCategoryId() == 0) {
            ValidatorUtils.validate(postsPush, AddGroup.class);
        }

        posts.setPostId(postsId);
        posts.setTitle(postsPush.getTitle());
        posts.setCover(postsPush.getCover());
        posts.setImageUrl(postsPush.getImageUrl());
        posts.setCategoryId(postsPush.getCategoryId() == null ? 0L : postsPush.getCategoryId());
        posts.setContent(postsPush.getContent());
        posts.setUserId(LoginHelper.getUserId());
        posts.setReleaseFlag(postsPush.getReleaseFlag());
        posts.setEditTime(new Date());
        posts.setRemark(postsPush.getRemark());

       if (ObjectUtils.isEmpty(postsId)) {
           baseMapper.insert(posts);
       } else {
            baseMapper.updateById(posts);
       }

        // 删除原来的
        if (!ObjectUtils.isEmpty(posts.getPostId())) {
            tagsMapper.deletePostTagByPostsId(posts.getPostId(), null);
        }
        // 4. 更新帖子相关的标签
        if (CollectionUtil.isNotEmpty(postsPush.getTags())) {
            // 查询
            List<Long> tags = tagsMapper.selectListByTagName(postsPush.getTags()).stream().map(Tags::getTagId).toList();
            tagsMapper.insertPostTag(posts.getPostId(), tags);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(DelParam delForm, Long userId) {
        List<Long> postsIds = delForm.getIds();
        // 1. 评论表
        LambdaQueryWrapper<CommentPosts> commentLqw = new LambdaQueryWrapper<CommentPosts>()
            .in(CommentPosts::getPostsId, postsIds);
        commentPostsMapper.delete(commentLqw);
        // 2. 收藏表
        LambdaQueryWrapper<CollectPosts> collectLqw = new LambdaQueryWrapper<CollectPosts>();
        collectLqw.in(CollectPosts::getPostId, postsIds);
        // 3. 点赞表
        LambdaQueryWrapper<LikePosts> likeLqw = new LambdaQueryWrapper<LikePosts>();
        likeLqw.in(LikePosts::getPostId, postsIds);
        // 4. 标签表
        tagsMapper.deleteWithPostTag(postsIds);
        // 5。 帖子表
        LambdaQueryWrapper<Posts> postsLqw = new LambdaQueryWrapper<Posts>();
        postsLqw.in(Posts::getPostId, postsIds)
                .eq(Posts::getUserId, userId);
        int delete = baseMapper.delete(postsLqw);
        if (delete != postsIds.size()) {
            throw new FrontException("front.delete.error");
        }
    }

    @Override
    public TableDataInfo<PostsVo> queryCollectPageList(PageQuery pageQuery, Long userId) {

        Page<CollectPostsVo> collectPage = collectPostsMapper.selectVoPage(pageQuery.build(),
            new LambdaQueryWrapper<CollectPosts>().eq(CollectPosts::getUserId, userId));

        List<Long> postsIds = collectPage.getRecords().stream().map(CollectPostsVo::getPostId).toList();
        List<PostsVo> data = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(postsIds)) {
            data = baseMapper.selectVoList(new LambdaQueryWrapper<Posts>()
                .in(Posts::getPostId, postsIds));

            for (PostsVo datum : data) {
                List<String> tags = tagsMapper.selectPostTagList(datum.getPostId());
                datum.setTags(tags);
            }
        }

        Page<PostsVo> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
        page.setRecords(data);
        page.setTotal(collectPage.getTotal());
        return TableDataInfo.build(page);
    }

    /**
     * 更新帖子action事件触发器
     *
     * @param event 事件触发对象
     */
    @Async
    @EventListener
    public void updatePostsEvent(UpdatePostsEvent event) {
        // 更新
        PostsVo postsVo = queryById(event.getPostsId());
        if (ObjectUtils.isEmpty(postsVo)) {
            return;
        }

        PostsBo postsBo = new PostsBo();
        postsBo.setPostId(postsVo.getPostId());

        String type = event.getType();
        long count = RecommendScore.COUNT_INCREMENT;
        int num = 0;

        switch (type) {
            case ActionTypes.LIKE:
                num = RecommendScore.LIKE_SCORE;
                count = event.getPlusFlag() ? postsVo.getLikeCount() + count : postsVo.getLikeCount() - count;
                postsBo.setLikeCount(count);
                break;
            case ActionTypes.COLLECT:
                num = RecommendScore.COLLECT_SCORE;
                count = event.getPlusFlag() ? postsVo.getCollectCount() + count : postsVo.getCollectCount() - count;
                postsBo.setCollectCount(count);
                break;
            case ActionTypes.VIEW:
                count = event.getPlusFlag() ? postsVo.getViewCount() + count : postsVo.getViewCount() - count;
                postsBo.setViewCount(count);
                num = RecommendScore.VIEW_SCORE;
                break;
            case ActionTypes.COMMENT:
                count = event.getPlusFlag() ? postsVo.getCommentCount() + count : postsVo.getCommentCount() - count;
                postsBo.setCommentCount(count);
                break;
            default:
                throw new FrontException("front.action.type.exists", type);
        }
        postsBo.setRecommendTime(postsBo.getRecommendTime() == null ? Instant.now().getEpochSecond() : null);
        int score = event.getPlusFlag() ? postsVo.getRecommendScore() + num : postsVo.getRecommendScore() - num;
        postsBo.setRecommendScore(score);

        updateByBo(postsBo);
    }

}
